package net.pragyah.scalby.inference.approximate


import net.pragyah.scalgorithms.graphs._
import net.pragyah.scalgorithms.graphs.search._




class LogicSampling[A](val G:Graph[Variable[A]],P : ProbabilityDistribution[A], val aA:Set[Variable[A]]) {
  
  aA.foreach(v => assume(v() != None))
  /**
   * Estimate by generating the samples m times
   */
  def estimate(m:int):Map[Variable[A],Map[A,double]] = {
    
    var estimates = Map[Variable[A],Map[A,double]]();
    //order  the n nodes in V in an ancestral ordering 
    val sorted = new DFS().topologicallySort(G)
    
    sorted.filter(X => !aA.contains(X.data)).foreach(X =>{

      var xMap = Map[A,double]()
      X.data.values.foreach(x => {
        xMap = xMap + ((x,0))
      })
      estimates = estimates + (X.data -> xMap)
     }
    )
    
    val n = sorted.size
    var sofar = Map[Variable[A],A]();
    
    for (i <- 1 to m){
      var j = 1
      while(j <= n){
        var af = List[Tuple2[A,float]]()
        
        val Xj = sorted(j).data
        
        //for each value of Xj ... find out the probability given the values of parent variables from the ProbabilityDistribution
        val values = Xj.values
        values.foreach(value => {
          
          
          val dependencies = P.getDependencies(Xj,value)
          
          //TODO ... for the  "sofar" figured out values of the parent variabls ..
          //(they are sorted.. remember? so the ones handled so far are inclusive of Xj's parents) ...
            //find out the probability of each value of Xj
          dependencies.keySet.foreach(mapXx =>{

                                        
                                      })
          
          
          
          val vp = 0f;  //find out the probability for this value given the values of parent variables
          
          
          af = af + (value -> vp)
                       })
        
        val p = Math.random.toFloat; //generate a random value in between 0 and 1 

        //now using the randomly generated probability value p .... find out the corresponding value ...simulating a Roulette Wheel  selection
        var xj:Option[A] = None
        var sum = 0f
        for(vf <- af 
            if xj == None){
          sum = sum + vf._2
          if(p < sum) xj = Some(vf._1)
        }
        
        if(aA.contains(Xj) && xj.get != Xj()){
          j = 1 //restart
          sofar = Map[Variable[A],A]() //reset
        }else{
          sofar = sofar + (Xj -> xj.get)
          j = j + 1
        }
      }
      
      sorted.filter(X => !aA.contains(X.data)).foreach(X => {
        val Xj = X.data
        val xj = sofar(Xj)
        //add 1 to the number of occurance of xj .... 
        estimates(Xj)(xj) = estimates(Xj)(xj) +1
	  })
      
    }
    
    estimates.keys.filter(X => !aA.contains(X)).foreach(X =>{
      estimates(X).keys.foreach( xj => {
        estimates(X)(xj) = estimates(X)(xj)/m 
                                 })
      }
    )
    estimates
  }

}
